/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.cluster.strictorder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.integration.Message;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
/**
* @author Gary Russell
*
*/
public class JdbcEntityQueues implements EntityQueues<String, Message<?>>, Trigger {
private String dispatcherName;
private JdbcOperations jdbcOperations;
private List<String> initializationKeys;
private Log logger = LogFactory.getLog(getClass());
public JdbcEntityQueues(JdbcOperations jdbcOperations, String dispatcherName) {
this.jdbcOperations = jdbcOperations;
this.dispatcherName = dispatcherName;
initializationKeys = new LinkedList<String>(keySet());
}
public Date nextExecutionTime(TriggerContext triggerContext) {
if (this.initializationKeys.size() > 0) {
return new Date(System.currentTimeMillis() + 1000);
}
return null;
}
public void setCapacity(int capacity) {
throw new UnsupportedOperationException();
}
protected byte[] getBlobAsBytes(ResultSet rs)
throws SQLException {
return new DefaultLobHandler().getBlobAsBytes(rs, "message");
}
public Object remove(String key) {
try {
MessageWrapper mw = this.jdbcOperations.queryForObject(getFirstSql(),
new RowMapper<MessageWrapper>() {
public MessageWrapper mapRow(ResultSet rs, int rowNum)
throws SQLException {
try {
byte[] bytes = getBlobAsBytes(rs);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return new MessageWrapper(rs.getLong("id"),
(Message<?>) ois.readObject());
} catch (IOException e) {
throw new SQLException(e);
} catch (ClassNotFoundException e) {
throw new SQLException(e);
}
}
}, key);
this.jdbcOperations.update("delete from queued where id = ?", mw.getId());
return mw.getMessage();
}
catch (EmptyResultDataAccessException e) {
return null;
}
}
protected String getFirstSql() {
return "select id, " +
"entity_id, process_id, message from queued where entity_id = ? " +
"order by id limit 1";
}
public int size(String key) {
return this.jdbcOperations.queryForInt("select count(id) from queued where entity_id = ?",
key);
}
public Set<String> keySet() {
List<String> keys = this.jdbcOperations.query("select distinct entity_id from queued " +
"where process_id = ?",
new RowMapper<String>() {
public String mapRow(ResultSet rs, int rowNum)
throws SQLException {
return rs.getString("entity_id");
}},
this.dispatcherName);
return new HashSet<String>(keys);
}
public void add(final String key, Message<?> message) {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(message);
} catch (IOException e) {
throw new RuntimeException(e);
}
this.jdbcOperations.execute(getInsertSql(),
new AbstractLobCreatingPreparedStatementCallback(getLobHandler()) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator)
throws SQLException, DataAccessException {
ps.setString(1, key);
ps.setString(2, dispatcherName);
lobCreator.setBlobAsBytes(ps, 3, bos.toByteArray());
}
});
}
/**
* @return
*/
protected String getInsertSql() {
return "insert into queued " +
"(entity_id, process_id, message) " +
"values(?, ?, ?)";
}
protected LobHandler getLobHandler() {
return new DefaultLobHandler();
}
public String nextStartUpKey() {
if (this.initializationKeys.size() < 1) {
return null;
}
String key = this.initializationKeys.remove(0);
logger.debug("Returning initial key:" + key);
return key;
}
private class MessageWrapper {
private long id;
private Message<?> message;
public MessageWrapper(long id, Message<?> message) {
this.id = id;
this.message = message;
}
public long getId() {
return id;
}
public Message<?> getMessage() {
return message;
}
}
}